#include "bV2.hh"

using namespace std;
using boost::optional;

KeywordMapType default_keywords = {
  { "CLR", K_CLR },
  { "DATA", K_DATA },
  { "END", K_END },
  { "FOR", K_FOR },
  { "GOSUB", K_GOSUB },
  { "GOTO", K_GOTO },
  { "IF", K_IF }, 
  { "INPUT", K_INPUT },
  { "LET", K_LET },
  { "LIST", K_LIST },
  { "NEXT", K_NEXT },
  { "ON", K_ON },
  { "PEEK", K_PEEK },
  { "POKE", K_POKE },
  { "PRINT", K_PRINT },
  { "PROC", K_PROC },
  { "READ", K_READ },
  { "REM", K_REM },
  { "RESTORE", K_RESTORE },
  { "RETURN", K_RETURN },
  { "RUN", K_RUN },
  { "STEP", K_STEP },
  { "THEN", K_THEN },
  { "TO", K_TO },
  //functions
  { "DEC", F_DEC },
  { "INT", F_INT },
  { "SIN", F_SIN },
  { "COS", F_COS },
  //string functions
  { "STR$", F_STR },
  { "HEX$", F_HEX },
  { "MID$", F_MID }
};

TokNum::TokNum(int x) : n(x) {}
TokNum::TokNum(const string &x) {
  istringstream in(x);
  if(!(in >> n)) throw invalid_argument("not an int: " + x);
}
string TokNum::str() const {
  ostringstream o;
  o << n;
  return o.str();
}
float TokNum::as_float() const { return n; };

TokFloat::TokFloat(float x) : n(x) {}
TokFloat::TokFloat(const string &x) {
  istringstream in(x);
  if(!(in >> n)) throw invalid_argument("not an int: " + x);
}
string TokFloat::str() const {
  ostringstream o;
  o << n;
  return o.str();
}
float TokFloat::as_float() const { return n; };

TokString::TokString(const string &x) : s(x) {}
string TokString::str() const { return '"' + s + '"'; }

TokCmd::TokCmd(const KeywordMapType &keywords, const string &x) : identifier(x) {
  boost::to_upper(identifier);
  auto found(keywords.find(identifier));
  if(found == keywords.end()) throw invalid_argument("unknown command: " + x);
  cmd = found->second;
}
TokCmd::TokCmd(keyword_e x) : cmd(x) {}
string TokCmd::str() const { return identifier; }

TokVar::TokVar(const string &x) : var(x) {}
string TokVar::str() const { return var; }

TokOperators::TokOperators(char x) : op(x) {}
string TokOperators::str() const { return string(1, op); }

TokRelOp::TokRelOp(char x) : TokOperators(x) {}
string TokRelOp::str() const {
  switch(op) {
  case '[':
    return "<=";
  case ']':
    return ">=";
  case '!':
    return "<>";
  default:
    return string(1, op);
  }
}

TokPar::TokPar(char x) : par(x) {}
string TokPar::str() const { return string(1, par); }

TokSep::TokSep(char x) : sep(x) {}
string TokSep::str() const { return string(1, sep); }

TokenListType tokenise(const KeywordMapType &keywords, const string &line) {
  TokenListType toks;
  unsigned beg;
  for(unsigned i = 0; i < line.size(); ++i) {
    //cout << "i = " << i << endl;
    while(i < line.size() && isblank(line[i])) ++i;
    if(i >= line.size()) break;
    beg = i;
    if(isalpha(line[i])) {
      char var_type = '\0';
      while(i < line.size()) {
	var_type = line[i];
	if(isalnum(var_type)) { //I0, I1, I2, etc...
	  ++i;
	} else {
	  if(var_type == '$' || var_type == '%') ++i;
	  break;
	} //Token ends there, e.g. I%, HEX$, etc...
      }
      string tmp(line.substr(beg, i - beg));
      try {
	toks.push_back(new TokCmd(keywords, tmp));
	// cout << "Cmd " << tmp << endl;
      }
      catch(const std::invalid_argument &) {
	if(var_type == '$') {
	  toks.push_back(new TokStringVar(tmp));
	} else if(var_type == '%') {
	  toks.push_back(new TokIntVar(tmp));
	} else {
	  toks.push_back(new TokFloatVar(tmp));
	}
      }
      --i; //Push char back
    } else if(isdigit(line[i])) {
      while(i < line.size() && isdigit(line[i])) ++i;
      if(i < line.size() && (line[i] == '.' || toupper(line[i]) == 'E')) {
	do {
	  ++i;
	} while(i < line.size() && (isdigit(line[i]) || toupper(line[i]) == 'E' || line[i] == '+' || line[i] == '-'));
	string tmp(line.substr(beg, i - beg));
	toks.push_back(new TokFloat(tmp));
      } else {
	string tmp(line.substr(beg, i - beg));
	// cout << "Num " << tmp << endl;
	toks.push_back(new TokNum(tmp));
      }
      --i; //Push char back
    } else if(strchr("+-*/", line[i])) {
      // cout << "Operator " << line[i] << endl;
      toks.push_back(new TokOp(line[i]));
    } else if(strchr("<>", line[i])) {
      char c = line[i];
      if(i + 1 < line.size() && strchr(">=", line[i + 1])) {
	++i;
	if(c == '<' && line[i] == '>') c = '!';
	else if(c == '<' && line[i] == '=') c = '[';
	else if(c == '>' && line[i] == '=') c = ']';
	else --i;
      }
      toks.push_back(new TokRelOp(c));
    } else if(strchr("()", line[i])) {
      // cout << "Parenthesis " << line[i] << endl;
      TokPar *ptr;
      if(line[i] == '(') 
	ptr = new TokParOpen(line[i]);
      else
	ptr = new TokParClose(line[i]);
      toks.push_back(ptr);
    } else if(line[i] == '=') {
      toks.push_back(new TokEqual());
      // cout << "Assignment " << line[i] << endl;
    } else if(line[i] == '"') {
      beg = ++i;
      while(i < line.size() && line[i] != '"') ++i;
      string tmp(line.substr(beg, i - beg));
      // cout << "String " << tmp << endl;
      toks.push_back(new TokString(tmp));
    } else if(line[i] == ',') {
      toks.push_back(new TokSep(line[i]));
    } else {
      ostringstream o;
      o << "parse error at pos " << i;
      throw std::runtime_error(o.str());
    }
  }
  return toks;
}


bool Basic::running() const { return curline != prg.end(); }

Basic::FloatType Basic::parenthesis() {
  if(beg == end || !(dynamic_cast<const TokParOpen *>(*beg))) throw SyntaxError(); else {
    ++beg;
    FloatType x(numexpr());
    if(!(dynamic_cast<const TokParClose *>(*beg)))
      throw UnbalancedParenthesisError();
    else
      ++beg; //Increse late so that the error message prints the right token
    return x;
  }
}

Basic::FloatType Basic::term() {
#ifdef DEBUG
  cout << "term: " << (*beg)->str() << endl;
#endif
  //Check if there is a term
  if(beg == end) throw MissingTermError();
  //Term is a variable?
  if(const TokFloatVar *var = dynamic_cast<const TokFloatVar*>(*beg)) {
    VarMapType::const_iterator found(varsf.find(var->var));
    if(found == varsf.end()) throw UndefinedVariableError();
    ++beg;
    return found->second;
  } else if(const TokIntVar *var = dynamic_cast<const TokIntVar*>(*beg)) {
    IntVarMapType::const_iterator found(varsi.find(var->var));
    if(found == varsi.end()) throw UndefinedVariableError();
    ++beg;
    return static_cast<FloatType>(found->second);
  }
  //Term is a constant?
  if(const TokNumeric *var = dynamic_cast<const TokNumeric*>(*beg)) {
    ++beg;
    return var->as_float();
  }
  //Term is '(' numexpr ')'
  if(dynamic_cast<const TokParOpen *>(*beg)) return parenthesis();
  //A function call?
  if(dynamic_cast<const TokCmd *>(*beg)) {
    return exec_funcall();
  }
  const TokOp* op;
  if((op = dynamic_cast<const TokOp*>(*beg)) && op->op == '-') { //unary minus
    ++beg;
    return -term();
  }
  if(dynamic_cast<const TokString *>(*beg) || dynamic_cast<const TokStringVar *>(*beg)) {
    throw TypeMismatchError();
  }
  throw MissingTermError();
}

Basic::FloatType Basic::numexpr() {
#ifdef DEBUG
  cout << "numexpr: " << (*beg)->str() << endl;
#endif
  if(beg == end) throw NumericValueExpectedError();
  FloatType x(numexpr2());
  if(beg == end) return x;
  const TokOp* op;
  if(!(op = dynamic_cast<const TokOp*>(*beg))) {
    //Something else. As we got a term already, this is fine, too.
    return x;
  }
  switch(op->op) {
  case '+':
    ++beg;
    return x + numexpr();
  case '-':
    ++beg;
    return x - numexpr();
    //case '%':
    //return TokFloat(x.n % y.n);
  default:
    return numexpr3(x);
  }
  throw SyntaxError();
}

Basic::FloatType Basic::numexpr2() {
  FloatType x(term());
  return numexpr3(x);
}

Basic::FloatType Basic::numexpr3(FloatType x) {
  const TokOp* op;
  if(beg == end) return x;
  else if(!(op = dynamic_cast<const TokOp*>(*beg))) return x;
  else ++beg;
  if(beg == end) {
    throw OperandMissingError();
  } else {
    switch(op->op) {
    case '*':
      x *= numexpr2();
      break;
    case '/':
      x /= numexpr2();
      break;
    default:
      --beg;
    }
    return x;
  }
  throw SyntaxError();
}
 
bool Basic::relation() {
  FloatType x(numexpr());
  if(beg == end) throw OperatorMissingError();
  const Token *op(*beg++); //dynamic_pointer_cast<TokOp>(*beg));
  if(beg == end) throw OperandMissingError();
  FloatType y(numexpr());
  if(dynamic_cast<const TokEqual*>(op)) {
    return x == y;
  } else {
    const TokRelOp *op2(dynamic_cast<const TokRelOp*>(op));
    if(!op2) throw MalformedRelationError();
    switch(op2->op) {
    case '<':
      return x < y;
    case '>':
      return x > y;
    case '!':
      return x != y;
    case '[':
      return x <= y;
    case ']':
      return x >= y;
    }
  }
  throw invalid_argument("invalid relation");
}

string Basic::exec_strfun(int cmd) {
  char buf[32];

  switch(cmd) {
  case F_HEX:
    sprintf(buf, "%04X", static_cast<unsigned int>(numexpr()));
    return buf;
  case F_MID:
    return exec_mid();
  case F_STR:
    sprintf(buf, "%g", numexpr());
    return buf;
  default:
    throw SyntaxError();
  }
  //throw logic_error("exec_strfun");
}

string Basic::exec_mid() {
  //			MID$(strexpr, start [, len])
  //cope only with:	     ^^^^^^^^^^^^^^^^^^^^^^
  const TokStringVar *svar;
  const TokString *scon;
  string str;

  if(!(svar = dynamic_cast<const TokStringVar*>(*beg))) {
    if(scon = dynamic_cast<const TokString *>(*beg)) {
      str = scon->s;
    } else {
      throw TypeMismatchError();
    }
  } else {
    auto found = varss.find(svar->var);
    if(found == varss.end()) throw UndefinedVariableError();
    str = found->second;
  }
  if(!(dynamic_cast<const TokSep *>(*++beg))) throw SyntaxError(); else {
    auto oldbeg = ++beg;
    int pos(numexpr());
    if(--pos < 0) {
      beg = oldbeg; //More useful hint
      throw IllegalQuantityError();
    } else if(static_cast<unsigned int>(pos) >= str.size()) {
      str.clear();
    } else {
      str = str.substr(pos);
    }
  }
  return str;
}

/* \brief string expression
 *
 * strexpr: String | String * term | String + strexpr | numexpr | strfunction ( strexpr )
 */
string Basic::strexpr() {
  const TokCmd *cmd;
  string ls;

  if(const TokString *x = dynamic_cast<const TokString*>((*beg))) {
    ls = x->s;
  } else if(const TokStringVar *x = dynamic_cast<const TokStringVar*>((*beg))) {
    auto found = varss.find(x->var);
    if(found == varss.end()) throw UndefinedVariableError();
    ls = found->second;
  } else if((cmd = dynamic_cast<const TokCmd *>(*beg)) && (cmd->cmd >= F_STR)) {
    if(++beg == end || !(dynamic_cast<const TokParOpen *>(*beg++))) throw SyntaxError();
    ls = exec_strfun(cmd->cmd);
    if(beg == end || !(dynamic_cast<const TokParClose *>(*beg))) throw UnbalancedParenthesisError();
  } else {
    try {
      FloatType x(numexpr());
      ostringstream out;
      out << x;
      return out.str();
    }
    catch(const MissingTermError &) {
      //A missing term is just a syntax error...
      throw SyntaxError();
    }
  }
  if(++beg == end) {
    return ls;
  } else {
    const TokOp *op = dynamic_cast<const TokOp*>(*beg);
    if(!op)
      return ls;
    else {
      ++beg;
      switch(op->op) {
      case '*':
	{
	  int count = static_cast<int>(numexpr());
	  string tmp;
	  for(auto i = 0; i < count; ++i) tmp += ls;
	  return tmp;
	}
      case '+':
	return ls + strexpr();
      default:
	return ls;
      }
    }
  }
}

Basic::FloatType Basic::exec_funcall() {
  const TokCmd *cmd;
  float ret;

#ifdef DEBUG
  cout << "exec_funcall: " << (*beg)->str() << endl;
#endif
  if(beg == end ||
     !(cmd = dynamic_cast<const TokCmd*>(*beg)) ||
     (cmd->cmd < KF_NUMERIC_FUNCTIONS) ||
     (cmd->cmd >= KF_STRING_FUNCTIONS)) throw SyntaxError(); else {
    ++beg;
    if(cmd->cmd == F_DEC) { //Special case, we get a string but return a number...
      if(beg == end || !dynamic_cast<const TokParOpen *>(*beg++)) throw SyntaxError();
      string s(strexpr());
      if(beg == end || !dynamic_cast<const TokParClose *>(*beg++)) throw UnbalancedParenthesisError();
      long l;
      istringstream in(s);
      in >> hex >> l;
      return l;
    }
    FloatType par(parenthesis());
    switch(cmd->cmd) {
    case F_INT:
      ret = floorf(par);
      break;
    case F_COS:
      ret = cosf(par);
      break;
    case F_SIN:
      ret = sinf(par);
      break;
    default:
      throw SyntaxError();
    }
  }
  return ret;
}

void Basic::exec_print() {
  string sexpr;

  while(beg != end) {
    sexpr = strexpr();
    cout << sexpr;
  }
  cout << '\n';
}

void Basic::exec_assignment() {
  if(beg == end) throw VariableExpectedError(); else {
    if(const TokIntVar*x = dynamic_cast<const TokIntVar*>(*beg)) {
      if(++beg == end || !(dynamic_cast<const TokEqual*>(*beg++))) throw OperatorMissingError();
      FloatType y(numexpr());
      varsi[x->var] = y;
    } else if(const TokFloatVar *x = dynamic_cast<const TokFloatVar*>(*beg)) {
      if(++beg == end || !(dynamic_cast<const TokEqual*>(*beg++))) throw OperatorMissingError();
      FloatType num(numexpr());
      varsf[x->var] = num;
    } else if(const TokStringVar *x = dynamic_cast<const TokStringVar*>(*beg)) {
      if(++beg == end || !(dynamic_cast<const TokEqual*>(*beg++))) throw OperatorMissingError();
      string y(strexpr());
      varss[x->var] = y;
    } else {
      //No variable found
      throw SyntaxError();
    }
  }
}

void Basic::exec_for() {
  //FOR var = expr TO expr (STEP expr)
  const TokIntVar *ivar = NULL;
  const TokFloatVar *fvar = NULL;
  const TokCmd *cmd;
  TokenListType::const_iterator oldbeg(beg);

  try {
    if(beg == end || (
		      !(ivar = dynamic_cast<const TokIntVar*>(*beg)) &&
		      !(fvar = dynamic_cast<const TokFloatVar*>(*beg))
		      )
       ) throw VariableExpectedError();
    if(++beg == end || !(dynamic_cast<const TokEqual*>(*beg))) throw OperatorMissingError();
    if(++beg == end) throw SyntaxError();
    TokFloat from(numexpr());
    float step = 1.0f;
    if(beg == end ||
       !((cmd = dynamic_cast<const TokCmd*>(*beg++))
	 && cmd->cmd == K_TO)) throw SyntaxError();
    if(beg == end) throw SyntaxError();
    TokFloat to(numexpr());
    if(beg != end) {
      if(!(cmd = dynamic_cast<const TokCmd*>(*beg++)) || cmd->cmd != K_STEP) throw SyntaxError();
      TokFloat steptok(numexpr());
      step = steptok.n;
    }
    if(fvar) {
      call_stack.push_back(new CallStackFornext(curline, fvar->var, step, to.n));
      varsf[fvar->var] = from.n;
    } else if(ivar) {
      call_stack.push_back(new CallStackFornextI(curline, ivar->var, step, to.n));
      varsi[ivar->var] = from.n;
    } else throw logic_error("ivar and fvar is NULL");
  }
  catch(...) {
    beg = oldbeg; //Restore token!
    throw; 
  }
  assert(!call_stack.empty());
}

void Basic::exec_next() {
  //NEXT (i)
  TokenListType::const_iterator oldbeg(beg);

  try {
    CallStackFornext *stackptr = NULL;
    CallStackFornextI *stackptri = NULL;
    
    if(call_stack.empty()) throw StackUnderflowError();
    if(!(stackptr = dynamic_cast<CallStackFornext *>(&call_stack.back())) && !(stackptri = dynamic_cast<CallStackFornextI *>(&call_stack.back()))) throw NextWithoutForError();
    if(beg != end) {
      if(stackptr) {
	if(dynamic_cast<const TokIntVar*>(*beg)) throw MismatchedVariableError();
	if(const TokFloatVar *var = dynamic_cast<const TokFloatVar*>(*beg++)) {
	  if(var->var != stackptr->variable) throw MismatchedVariableError();
	} else throw VariableExpectedError();
      } else { //stackptri!
	if(dynamic_cast<const TokFloatVar*>(*beg)) throw MismatchedVariableError();
	if(const TokIntVar *var = dynamic_cast<const TokIntVar*>(*beg++)) {
	  if(var->var != stackptri->variable) throw MismatchedVariableError();
	} else throw VariableExpectedError();
      }
    }
    if(stackptr) {
      if((varsf[stackptr->variable] += stackptr->increment) <= stackptr->to) {
	curline = stackptr->lineno;
      } else call_stack.pop_back();
    } else { //stackptri!
      if((varsi[stackptri->variable] += stackptri->increment) <= stackptri->to) {
	curline = stackptri->lineno;
      } else call_stack.pop_back();
    }
  }
  catch(...) {
    beg = oldbeg; //Restore token!
    throw; 
  }
}

void Basic::exec_run() {
  exec_run(prg.begin(), true);
}
void Basic::exec_run(const map<unsigned short,TokenListType>::const_iterator &start_with, bool rescan_procedures) {
  try {
    //Look up all procedures!
    if(rescan_procedures) {
      for(curline = prg.begin(); curline != prg.end(); ++curline) {
	const TokenListType &alltokens(curline->second);
	TokenListType::const_iterator ltoks(alltokens.begin());
	const TokCmd *cmd(dynamic_cast<const TokCmd*>(*ltoks));
	if(cmd && cmd->cmd == K_PROC) {
	  const TokVar *var;
	  if(++ltoks == alltokens.end()) throw VariableExpectedError();
	  else if(var = dynamic_cast<const TokIntVar*>(*ltoks)) {
	    varsi[var->var] = curline->first;
	  } else if(var = dynamic_cast<const TokFloatVar*>(*ltoks)) {
	    varsf[var->var] = curline->first;
	  } else throw VariableExpectedError();
	}
      }
    }
    //Prepare!
    curline = start_with;
    call_stack.clear();
    exec_restore();
    //Run the program!
    while(curline != prg.end()) {
      //cout << "\t\t\t\t\t" << curline->first << endl;
      const TokCmd *cmd(statement(curline->second.begin(), curline->second.end()));
      if(cmd && cmd->cmd == K_GOTO) continue; //Do not increment line number!
      ++curline;
    }
  }
  catch(...) {
    cerr << "Error in line " << curline->first << "!\n";
    throw;
  }
}

void Basic::internal_jump_to_line(unsigned short linenum, bool push_stack) {
  map<unsigned short, TokenListType >::const_iterator line(prg.find(linenum));
  if(line != prg.end()) {
    if(push_stack) call_stack.push_back(new CallStackElement(curline));
    //else goto
    curline = line;
  } else {
    throw UndefinedStatementError();
  }
}

void Basic::exec_gosub() {
  if(beg == end) throw SyntaxError();
  TokFloat target(numexpr());
  internal_jump_to_line(target.n, true);
}

void Basic::exec_goto() {
  if(beg == end) throw SyntaxError();
  TokFloat target(numexpr());
  internal_jump_to_line(target.n, false);
}

void Basic::exec_on() {
  if(beg == end) throw SyntaxError();
  int target = numexpr(); //We need an integer...
  const TokCmd *cmd;
  if(target > 0) {
    if(beg == end ||
       !(cmd = dynamic_cast<const TokCmd *>(*beg))
       || !(cmd->cmd == K_GOSUB || cmd->cmd == K_GOTO)) throw SyntaxError();
    //cout << "*beg=" << (*beg)->str() << endl;
    ++beg;
    while(target > 0) { //Read that many...
      TokFloat line(term()); //Old basic hat constants only... we are better!
      if(--target == 0) {
	internal_jump_to_line(line.n, cmd->cmd == K_GOSUB);
	break; //the loop
      }
      if(beg == end) break; //If number is greater than available jump targets... just ignore!
      if(!(dynamic_cast<const TokSep *>(*beg++))) throw SyntaxError(); //comma needed
    }
  }
  beg = end; //Kill remaining tokens...
}

void Basic::exec_return() {
  if(call_stack.empty()) throw StackUnderflowError();
  CallStackElement &back = call_stack.back();
  curline = back.lineno;
  call_stack.pop_back(); //And frees memory?
}

const TokCmd *Basic::exec_ifthen() {
  if(beg == end) throw SyntaxError();
  bool rel = relation();
  if(beg == end) throw SyntaxError();
  const TokCmd *cmdthen = dynamic_cast<const TokCmd*>(*beg++);
  if(!cmdthen || cmdthen->cmd != K_THEN) throw SyntaxError();
  if(rel) return statement(); else beg = end; //Use up all tokens!
  return cmdthen;
}

void Basic::exec_list() {
  for(auto it : prg) {
    cout << it.first << ' ';
    for(auto j : it.second) {
      cout << ' ' << j->str();
    }
    cout << '\n';
  }
}

void Basic::exec_clear() {
  varsf.clear();
  varsf.clear();
  varss.clear();
}

void Basic::exec_new() {
  for(curline = prg.begin(); curline != prg.end(); ++curline) {
    for(auto i : curline->second) {
      delete i;
    }
  }
  prg.clear();
  curline = prg.end();
}

const Token *Basic::internal_read(bool need_sep) {
  while(dataline != prg.end()) {
    if(datatoken == dataline->second.begin()) {
      const TokCmd * cmd = dynamic_cast<const TokCmd *>(*datatoken);
      if(cmd && (cmd->cmd == K_DATA)) {
	if(++datatoken == dataline->second.end()) {
	  curline = dataline; //Error should be reported there...
	  throw SyntaxError();
	}
	break;
      }
    } else {
      if(++datatoken != dataline->second.end()) { //Still data in line
	if(need_sep) {
	  if(!dynamic_cast<const TokSep *>(*datatoken)) {
	    curline = dataline; //Error should be reported there...
	    throw SyntaxError();
	  }
	  ++datatoken;
	}
	if(datatoken == dataline->second.end()) {
	  curline = dataline; //Error should be reported there...
	  throw SyntaxError();
	}
	break;
      }
    }
    if(++dataline != prg.end())
      datatoken = dataline->second.begin();
  }
  if(dataline == prg.end()) throw OutOfDataError();
  return *datatoken;
}

void Basic::exec_read() {
  const Token *next;
  const TokOp *op;
  FloatType myf;

  if(beg == end) throw SyntaxError(); //Read what?
  do {
    int sign = 1;
    //cout << "Reading " << (*beg)->str() << endl;
    if(const TokIntVar *var = dynamic_cast<const TokIntVar *>(*beg)) {
      next = internal_read(true);
      if((op = dynamic_cast<const TokOp *>(next)) && op->op == '-') {
	sign = -1;
	next = internal_read(false);
      }
      const TokNum *tok = dynamic_cast<const TokNum *>(next);
      if(!tok) throw TypeMismatchError();
      varsi[var->var] = tok->n * sign;
    } else if(const TokFloatVar *var = dynamic_cast<const TokFloatVar *>(*beg)) {
      next = internal_read(true);
      //cout << "next " << next->str() << endl;
      if((op = dynamic_cast<const TokOp *>(next)) && op->op == '-') {
	sign = -1;
	next = internal_read(false);
      }
      //cout << "next " << next->str() << endl;
      const TokFloat *tok = dynamic_cast<const TokFloat *>(next);
      if(tok) {
	myf = tok->n;
      } else if(const TokNum *isint = dynamic_cast<const TokNum *>(next)) {
	myf = isint->n;
      } else throw TypeMismatchError();
      varsf[var->var] = myf * sign;
    } else if(const TokStringVar *var = dynamic_cast<const TokStringVar *>(*beg)) {
      const TokString *tok = dynamic_cast<const TokString *>(internal_read(true));
      if(!tok) throw TypeMismatchError();
      varss[var->var] = tok->s;
    } else throw SyntaxError();
    if(++beg == end) break;
    if(!dynamic_cast<const TokSep *>(*beg++)) throw SyntaxError();
  } while(true);
}

void Basic::exec_restore() {
  dataline = prg.begin();
  datatoken = dataline->second.begin(); //First token...
}
 
Basic::FloatType Basic::exec_peek() {
  throw IllegalQuantityError();
}

void Basic::exec_poke() {
  FloatType addr(numexpr());
  if(beg == end || !(dynamic_cast<const TokSep *>(*beg++))) throw SyntaxError();
  FloatType val(numexpr());
  exec_poke2(addr, val);
}

void Basic::exec_poke2(FloatType addr, FloatType val) {
  throw IllegalQuantityError();
}

const TokCmd *Basic::statement() {
  const TokCmd *cmd = NULL;

  if(beg == end) return cmd;
  TokenPtr tok = *beg;
  if((cmd = dynamic_cast<const TokCmd*>(tok))) {
    ++beg;
    switch(cmd->cmd) {
    case K_CLR:
      beg = end; //Use up all tokens!
      exec_clear();
      break;
    case K_END:
      beg = end; //Use up all tokens!
      curline = prg.end();
      return &default_goto_token;
      break;
    case K_GOSUB:
      exec_gosub();
      return &default_goto_token;
      break;
    case K_GOTO:
      exec_goto();
      break;
    case K_INPUT:
      throw runtime_error("not implemented");
      break;
    case K_LET:
      exec_assignment();
      break;
    case K_LIST:
      exec_list();
      break;
    case K_ON:
      exec_on();
      break;
    case K_PROC:
      if(curline == prg.end()) throw SyntaxError();
      beg = end;
      break;
    case K_DATA: //Also ignore rest of line...
    case K_REM: //Remark
      beg = end;
      break;
    case K_PEEK:
      throw SyntaxError();
    case K_POKE:
      exec_poke();
      break;
    case K_PRINT:
      exec_print();
      break;
    case K_READ:
      exec_read();
      break;
    case K_RESTORE:
      exec_restore();
      break;
    case K_RETURN:
      exec_return();
      break;
    case K_RUN:
      exec_run();
      break;
    case K_IF:
      return exec_ifthen();
      break;
    case K_FOR:
      exec_for();
      break;
    case K_NEXT:
      exec_next();
      break;
    default:
      unknown_command(); //No ... at beginning of statement.
    }
  } else {
    exec_assignment();
  }
  if(beg != end) {
    ostringstream oss;
    oss << "syntax error (left over tokens):";
    while(beg != end) oss << ' ' << (*beg++)->str();
    throw runtime_error(oss.str());
  }
  return cmd;
}

Basic::Basic() : default_goto_token(K_GOTO), curline(prg.end()) {}
Basic::~Basic() {
  //Free program token memory
  exec_new();
}

void Basic::add_line(unsigned short line, TokenListType &code) {
  auto oldline = prg.find(line);

  if(oldline != prg.end()) {
    for(auto i : oldline->second) delete i;
    oldline->second.clear();
  }
  prg[line] = code;
}

void Basic::run() {
  //cout << "void Basic::run()" << endl;
  exec_run();
}

void Basic::run(unsigned short line) {
  auto line_it = prg.find(line);
  //cout << "void Basic::run(unsigned short line): " << line_it->first << endl;
  if(line_it == prg.end()) throw UndefinedStatementError();
  exec_run(line_it, false);
}


unsigned long Basic::numvars() const { return varsf.size() + varsf.size() + varss.size(); }
const TokCmd *Basic::statement(TokenListType::const_iterator beg_, TokenListType::const_iterator end_) {
  beg = beg_;
  end = end_;
  return statement();
}
const Token *Basic::get_current_token() const { if(beg != end) return *beg; else return NULL; }
